using System;
using System.Collections.Generic;
using Gj.Galaxy.Logic;
using SimpleJSON;
using Unity.Mathematics;
using UnityEngine;
using Random = System.Random;

namespace Gj
{
    public enum EaseType
    {
        linear,
        spring,
        punch,
        easeInQuad,
        easeOutQuad,
        easeInOutQuad,
        easeInCubic,
        easeOutCubic,
        easeInOutCubic,
        easeInQuart,
        easeOutQuart,
        easeInOutQuart,
        easeInQuint,
        easeOutQuint,
        easeInOutQuint,
        easeInSine,
        easeOutSine,
        easeInOutSine,
        easeInExpo,
        easeOutExpo,
        easeInOutExpo,
        easeInCirc,
        easeOutCirc,
        easeInOutCirc,

        /* GFX47 MOD START */
        //bounce,
        easeInBounce,
        easeOutBounce,
        easeInOutBounce,

        /* GFX47 MOD END */
        easeInBack,
        easeOutBack,
        easeInOutBack,

        /* GFX47 MOD START */
        //elastic,
        easeInElastic,
        easeOutElastic,

        easeInOutElastic
        /* GFX47 MOD END */
    }

    public enum LoopType
    {
        /// <summary>
        ///     Do not loop.
        /// </summary>
        none,

        /// <summary>
        ///     Rewind and replay.
        /// </summary>
        loop,

        /// <summary>
        ///     Ping pong the animation back and forth.
        /// </summary>
        pingPong
    }

    public class MathTools
    {
        public static LoopType ConvertLoopType(iTween.LoopType t)
        {
            switch (t)
            {
                case iTween.LoopType.loop:
                    return LoopType.loop;
                case iTween.LoopType.pingPong:
                    return LoopType.pingPong;
                default:
                    return LoopType.none;
            }
        }

        public static EaseType ConvertEaseType(iTween.EaseType t)
        {
            switch (t)
            {
                case iTween.EaseType.linear:
                    return EaseType.linear;
                case iTween.EaseType.spring:
                    return EaseType.spring;
                case iTween.EaseType.punch:
                    return EaseType.punch;
                case iTween.EaseType.easeInQuad:
                    return EaseType.easeInQuad;
                case iTween.EaseType.easeOutQuad:
                    return EaseType.easeOutQuad;
                case iTween.EaseType.easeInOutQuad:
                    return EaseType.easeInOutQuad;
                case iTween.EaseType.easeInCubic:
                    return EaseType.easeInCubic;
                case iTween.EaseType.easeOutCubic:
                    return EaseType.easeOutCubic;
                case iTween.EaseType.easeInOutCubic:
                    return EaseType.easeInOutCubic;
                case iTween.EaseType.easeInQuart:
                    return EaseType.easeInQuart;
                case iTween.EaseType.easeOutQuart:
                    return EaseType.easeOutQuart;
                case iTween.EaseType.easeInOutQuart:
                    return EaseType.easeInOutQuart;
                case iTween.EaseType.easeInQuint:
                    return EaseType.easeInQuint;
                case iTween.EaseType.easeOutQuint:
                    return EaseType.easeOutQuint;
                case iTween.EaseType.easeInOutQuint:
                    return EaseType.easeInOutQuint;
                case iTween.EaseType.easeInSine:
                    return EaseType.easeInSine;
                case iTween.EaseType.easeOutSine:
                    return EaseType.easeOutSine;
                case iTween.EaseType.easeInOutSine:
                    return EaseType.easeInOutSine;
                case iTween.EaseType.easeInExpo:
                    return EaseType.easeInExpo;
                case iTween.EaseType.easeOutExpo:
                    return EaseType.easeOutExpo;
                case iTween.EaseType.easeInOutExpo:
                    return EaseType.easeInOutExpo;
                case iTween.EaseType.easeInCirc:
                    return EaseType.easeInCirc;
                case iTween.EaseType.easeOutCirc:
                    return EaseType.easeOutCirc;
                case iTween.EaseType.easeInOutCirc:
                    return EaseType.easeInOutCirc;
                case iTween.EaseType.easeInBounce:
                    return EaseType.easeInBounce;
                case iTween.EaseType.easeOutBounce:
                    return EaseType.easeOutBounce;
                case iTween.EaseType.easeInOutBounce:
                    return EaseType.easeInOutBounce;
                case iTween.EaseType.easeInBack:
                    return EaseType.easeInBack;
                case iTween.EaseType.easeOutBack:
                    return EaseType.easeOutBack;
                case iTween.EaseType.easeInOutBack:
                    return EaseType.easeInOutBack;
                case iTween.EaseType.easeInElastic:
                    return EaseType.easeInElastic;
                case iTween.EaseType.easeOutElastic:
                    return EaseType.easeOutElastic;
                case iTween.EaseType.easeInOutElastic:
                    return EaseType.easeInOutElastic;
                default:
                    return EaseType.linear;
            }
        }

        public static bool IsOdd(int num)
        {
            return num % 2 != 0;
        }

        // 取整并且设定保留位
        public static int Rounding(float num, int size = 10)
        {
            return (int)Math.Floor(num / size) * size;
        }

        public static double Radians(float angle)
        {
            return angle * (Math.PI / 180);
        }

        public static int FormatAngle(int angle)
        {
            if (angle >= 360) return angle - 360;
            return angle;
        }

        public static Vector3 GetThreePoint(Vector3 one, Vector3 two, float a)
        {
            var x1 = one.x;
            var y1 = one.z;
            var x2 = two.x;
            var y2 = two.z;

            //var a = Math.Atan2(y1 - y2, x1 - x2) * 180 / Math.PI;
            // var a = (one.z - two.z) / (one.x - two.x);
            // var l = Vector3.Distance(one, two);
            // var x = (float) (two.x + (l * Math.Cos(a + 60)));
            // var y = (float) (two.z + (l * Math.Sin(a + 60)));
            var x = (x2 - x1) * Math.Cos(a) - (y2 - y1) * Math.Sin(a);
            var y = (x2 - x1) * Math.Sin(a) + (y2 - y1) * Math.Cos(a);
            return new Vector3((float)x, 0, (float)y);
        }

        public static Vector3 CalculateCubicBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2)
        {
            var u = 1 - t;
            var tt = t * t;
            var uu = u * u;

            var p = uu * p0;
            p += 2 * u * t * p1;
            p += tt * p2;

            return p;
        }

        public static Vector3[] GetBezierList(Vector3 startPoint, Vector3 controlPoint, Vector3 endPoint,
            int segmentNum)
        {
            var path = new Vector3[segmentNum + 1];
            path[0] = startPoint;
            for (var i = 1; i <= segmentNum; i++)
            {
                var t = i / (float)segmentNum;
                var pixel = CalculateCubicBezierPoint(t, startPoint,
                    controlPoint, endPoint);
                path[i] = pixel;
            }

            return path;
        }

        public static float FormatAudioFader(int value)
        {
            if (value == 0) return -80;
            return Lerp(-40, 5, (float)value / 100);
        }

        public static Vector3 Rotate(Vector3 point, float angle)
        {
            return ToVector3(Rotate(ToVector2(point), angle));
        }

        public static Vector2 Rotate(Vector2 point, float angle)
        {
            float x = point.x * math.cos(angle) - point.y * math.sin(angle);
            float y = point.y * math.cos(angle) + point.x * math.sin(angle);
            return new Vector2(x, y);
        }

        public static bool Equal(float3 a, float3 b)
        {
            return a.x == b.x && a.y == b.y && a.z == b.z;
        }

        public static float GetAngle(Vector2Int a, Vector2Int b, Vector2Int c)
        {
            return Vector2.Angle(a - b, c - b);
        }

        public static float GetAngle(Vector3 a, Vector3 b, Vector3 c)
        {
            return Vector3.Angle(a - b, c - b);
        }

        public static float GetAngle(Vector2 a, Vector2 b)
        {
            var dir = a - b;
            dir = dir.normalized;
            var angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
            angle = angle < 0 ? angle + 360 : angle;
            return angle;
        }

        public static float GetVAngle(Vector3 a, Vector3 b)
        {
            var _a = new Vector2(0, a.y);
            var _b = new Vector2(Vector2.Distance(new Vector2(a.x, a.z), new Vector2(b.x, b.z)), b.y);
            return 180 - GetAngle(_a, _b);
        }

        public static float GetHAngle(Vector3 a, Vector3 b)
        {
            return GetAngle(new Vector2(a.x, a.z), new Vector2(b.x, b.z));
        }

        public static Vector3 RotationPoint(Vector3 c, Vector3 a, float angle)
        {
            return Quaternion.AngleAxis(angle, Vector3.up) * (a - c) + c;
        }

        public static byte GetDirection(Vector2Int c, Vector2Int o)
        {
            return HexCube.ToDirection(HexCube.FromOffset(c), HexCube.FromOffset(o));
        }

        public static Vector2Int GetOffsetPoint(Vector2Int c, Vector2Int o, bool b)
        {
            var cc = HexCube.FromOffset(c);
            var oo = HexCube.FromOffset(o);

            var direction = HexCube.ToDirection(cc, oo);
            if (b)
            {
                if (direction == 0) direction = 6;
                direction -= 1;
                return HexCube.AddOffset(cc, HexCube.Direction(direction)).ToOffset();
            }

            direction += 1;
            if (direction >= 6) direction = 0;
            return HexCube.AddOffset(cc, HexCube.Direction(direction)).ToOffset();
        }

        public static Vector3 GetMiddle(Vector2Int start, Vector2Int end)
        {
            return (HexCube.FromOffset(start).ToPosition() + HexCube.FromOffset(end).ToPosition()) / 2;
        }

        public static Vector3 GetMiddle(Vector3 start, Vector3 end)
        {
            return (start + end) / 2;
        }

        public static Vector3 GetPoint(Vector3 start, Vector3 end, float d)
        {
            return (end - start).normalized * d + start;
        }

        public static bool IsTurn(Vector2Int start, Vector2Int middle, Vector2Int end)
        {
            try
            {
                var enter = HexCube.ToDirection(HexCube.FromOffset(start), HexCube.FromOffset(middle));
                var export = HexCube.ToDirection(HexCube.FromOffset(middle), HexCube.FromOffset(end));
                var d = HexDirectionExtensions.OffsetDouble(enter, export);
                if (d == 3 || d == 9) d = 0;
                return d > 0 || enter / 6 != export / 6;
            }
            catch (Exception e)
            {
                return false;
            }
        }

        public static bool CheckSame(Vector2Int[] v1, Vector2Int[] v2)
        {
            if (v1.Length != v2.Length) return false;
            for (var i = 0; i < v2.Length; i++)
                if (v1[i] != v2[i])
                    return false;
            return true;
        }

        public static float3 SBLP(float3 a, float3 b, float3 c, float v)
        {
            var x = Lerp(a, b, v);
            var y = Lerp(b, c, v);
            return Lerp(x, y, v);
        }

        public static object[] ToVector2Obj(string s)
        {
            return ToVector2Obj(ToVector2(s));
        }

        public static object[] ToVector2Obj(Vector2Int p)
        {
            return new object[]{ p.x, p.y };
        }

        public static object ToVector2Byte(string s)
        {
            return ToVector2Byte(ToVector2(s));
        }

        public static object[] ToVector2Byte(string[] s)
        {
            var list = new object[s.Length];
            for (int i = 0; i < s.Length; i++)
            {
                list[i] = ToVector2Byte(s[i]);
            }
            return list;
        }

        public static object ToVector2Byte(Vector2Int p)
        {
            return SerializeInt.Vector2Byte(p);
        }

        public static Vector2Int ToVector2(object o)
        {
            return ToVector2((byte[])o);
        }

        public static Vector2Int ToVector2(byte[] o)
        {
            return SerializeInt.Byte2Vector(o);
        }

        public static Vector2Int ToVector2(object[] o)
        {
            return new Vector2Int((int) o[0], (int) o[1]);
        }

        public static Vector2Int ToVector2(string s)
        {
            var t = s.Split(',');
            return new Vector2Int(Convert.ToInt32(t[0]), Convert.ToInt32(t[1]));
        }

        public static string ToString(Vector2Int p)
        {
            return string.Format("{0},{1}", p.x, p.y);
        }

        public static Vector2 ToUIVector2(Vector3 v)
        {
            return new Vector2(v.x, v.y);
        }

        public static Vector3 ToUIVector3(Vector2 v)
        {
            return new Vector3(v.x, v.y, 0);
        }

        public static Vector2 ToVector2(Vector3 v)
        {
            return new Vector2(v.x, v.z);
        }

        public static Vector2Int ToVector2(Vector3Int v)
        {
            return new Vector2Int(v.x, v.z);
        }

        public static Vector3 ToVector3(Vector2 v)
        {
            return new Vector3(v.x, 0, v.y);
        }

        public static Vector3Int ToVector3(Vector2Int v)
        {
            return new Vector3Int(v.x, 0, v.y);
        }

        public static int Distance(int2 a, int2 b)
        {
            return math.abs(a.x - b.x) + math.abs(a.y - b.y);
        }

        public static Vector2 GetCenter(List<Vector2> vertices)
        {
            Vector2 sum = Vector2.zero;
            foreach (Vector2 vertex in vertices)
            {
                sum += vertex;
            }
            return sum / vertices.Count;
        }
    
        public static Vector3 GetCenter(List<Vector3> vertices)
        {
            Vector3 sum = Vector3.zero;
            foreach (Vector3 vertex in vertices)
            {
                sum += vertex;
            }
            return sum / vertices.Count;
        }

        public static float Normalized(float a, float b, float t)
        {
            if (t < a) return 0;
            if (t > b) return 1;
            return (t - a) / (b - a);
        }

        public static float Lerp(float a, float b, float t)
        {
            return a + (b - a) * t;
        }

        public static float2 Lerp(float2 a, float2 b, float t)
        {
            return new float2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t);
        }

        public static float3 Lerp(float3 a, float3 b, float t)
        {
            return new float3(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t);
        }

        public static float4 Lerp(float4 a, float4 b, float t)
        {
            return new float4(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t,
                a.w + (b.w - a.w) * t);
        }

        public static float3 Midpoint(float3 a, float3 b)
        {
            return (a + b) / 2;
        }

        public static float Compute(EaseType easeType, float start, float end, float value)
        {
            switch (easeType)
            {
                case EaseType.linear:
                    return linear(start, end, value);
                case EaseType.spring:
                    return spring(start, end, value);
                case EaseType.punch:
                    return spring(start, end, value); // TODO 
                case EaseType.easeInQuad:
                    return easeInQuad(start, end, value);
                case EaseType.easeOutQuad:
                    return easeOutQuad(start, end, value);
                case EaseType.easeInOutQuad:
                    return easeInOutQuad(start, end, value);
                case EaseType.easeInCubic:
                    return easeInCubic(start, end, value);
                case EaseType.easeOutCubic:
                    return easeOutCubic(start, end, value);
                case EaseType.easeInOutCubic:
                    return easeInOutCubic(start, end, value);
                case EaseType.easeInQuart:
                    return easeInQuart(start, end, value);
                case EaseType.easeOutQuart:
                    return easeOutQuart(start, end, value);
                case EaseType.easeInOutQuart:
                    return easeInOutQuart(start, end, value);
                case EaseType.easeInQuint:
                    return easeInQuint(start, end, value);
                case EaseType.easeOutQuint:
                    return easeOutQuint(start, end, value);
                case EaseType.easeInOutQuint:
                    return easeInOutQuint(start, end, value);
                case EaseType.easeInSine:
                    return easeInSine(start, end, value);
                case EaseType.easeOutSine:
                    return easeOutSine(start, end, value);
                case EaseType.easeInOutSine:
                    return easeInOutSine(start, end, value);
                case EaseType.easeInExpo:
                    return easeInExpo(start, end, value);
                case EaseType.easeOutExpo:
                    return easeOutExpo(start, end, value);
                case EaseType.easeInOutExpo:
                    return easeInOutExpo(start, end, value);
                case EaseType.easeInCirc:
                    return easeInCirc(start, end, value);
                case EaseType.easeOutCirc:
                    return easeOutCirc(start, end, value);
                case EaseType.easeInOutCirc:
                    return easeInOutCirc(start, end, value);
                case EaseType.easeInBounce:
                    return easeInBounce(start, end, value);
                case EaseType.easeOutBounce:
                    return easeOutBounce(start, end, value);
                case EaseType.easeInOutBounce:
                    return easeInOutBounce(start, end, value);
                case EaseType.easeInBack:
                    return easeInBack(start, end, value);
                case EaseType.easeOutBack:
                    return easeOutBack(start, end, value);
                case EaseType.easeInOutBack:
                    return easeInOutBack(start, end, value);
                case EaseType.easeInElastic:
                    return easeInElastic(start, end, value);
                case EaseType.easeOutElastic:
                    return easeOutElastic(start, end, value);
                case EaseType.easeInOutElastic:
                    return easeInOutElastic(start, end, value);
            }

            return 0;
        }

        public static float linear(float start, float end, float value)
        {
            return Mathf.Lerp(start, end, value);
        }

        public static float clerp(float start, float end, float value)
        {
            var min = 0.0f;
            var max = 360.0f;
            var half = Mathf.Abs((max - min) * 0.5f);
            var retval = 0.0f;
            var diff = 0.0f;
            if (end - start < -half)
            {
                diff = (max - start + end) * value;
                retval = start + diff;
            }
            else if (end - start > half)
            {
                diff = -(max - end + start) * value;
                retval = start + diff;
            }
            else
            {
                retval = start + (end - start) * value;
            }

            return retval;
        }

        public static float spring(float start, float end, float value)
        {
            value = Mathf.Clamp01(value);
            value = (Mathf.Sin(value * Mathf.PI * (0.2f + 2.5f * value * value * value)) * Mathf.Pow(1f - value, 2.2f) +
                     value) * (1f + 1.2f * (1f - value));
            return start + (end - start) * value;
        }

        public static float easeInQuad(float start, float end, float value)
        {
            end -= start;
            return end * value * value + start;
        }

        public static float easeOutQuad(float start, float end, float value)
        {
            end -= start;
            return -end * value * (value - 2) + start;
        }

        public static float easeInOutQuad(float start, float end, float value)
        {
            value /= .5f;
            end -= start;
            if (value < 1) return end * 0.5f * value * value + start;
            value--;
            return -end * 0.5f * (value * (value - 2) - 1) + start;
        }

        public static float easeInCubic(float start, float end, float value)
        {
            end -= start;
            return end * value * value * value + start;
        }

        public static float easeOutCubic(float start, float end, float value)
        {
            value--;
            end -= start;
            return end * (value * value * value + 1) + start;
        }

        public static float easeInOutCubic(float start, float end, float value)
        {
            value /= .5f;
            end -= start;
            if (value < 1) return end * 0.5f * value * value * value + start;
            value -= 2;
            return end * 0.5f * (value * value * value + 2) + start;
        }

        public static float easeInQuart(float start, float end, float value)
        {
            end -= start;
            return end * value * value * value * value + start;
        }

        public static float easeOutQuart(float start, float end, float value)
        {
            value--;
            end -= start;
            return -end * (value * value * value * value - 1) + start;
        }

        public static float easeInOutQuart(float start, float end, float value)
        {
            value /= .5f;
            end -= start;
            if (value < 1) return end * 0.5f * value * value * value * value + start;
            value -= 2;
            return -end * 0.5f * (value * value * value * value - 2) + start;
        }

        public static float easeInQuint(float start, float end, float value)
        {
            end -= start;
            return end * value * value * value * value * value + start;
        }

        public static float easeOutQuint(float start, float end, float value)
        {
            value--;
            end -= start;
            return end * (value * value * value * value * value + 1) + start;
        }

        public static float easeInOutQuint(float start, float end, float value)
        {
            value /= .5f;
            end -= start;
            if (value < 1) return end * 0.5f * value * value * value * value * value + start;
            value -= 2;
            return end * 0.5f * (value * value * value * value * value + 2) + start;
        }

        public static float easeInSine(float start, float end, float value)
        {
            end -= start;
            return -end * Mathf.Cos(value * (Mathf.PI * 0.5f)) + end + start;
        }

        public static float easeOutSine(float start, float end, float value)
        {
            end -= start;
            return end * Mathf.Sin(value * (Mathf.PI * 0.5f)) + start;
        }

        public static float easeInOutSine(float start, float end, float value)
        {
            end -= start;
            return -end * 0.5f * (Mathf.Cos(Mathf.PI * value) - 1) + start;
        }

        public static float easeInExpo(float start, float end, float value)
        {
            end -= start;
            return end * Mathf.Pow(2, 10 * (value - 1)) + start;
        }

        public static float easeOutExpo(float start, float end, float value)
        {
            end -= start;
            return end * (-Mathf.Pow(2, -10 * value) + 1) + start;
        }

        public static float easeInOutExpo(float start, float end, float value)
        {
            value /= .5f;
            end -= start;
            if (value < 1) return end * 0.5f * Mathf.Pow(2, 10 * (value - 1)) + start;
            value--;
            return end * 0.5f * (-Mathf.Pow(2, -10 * value) + 2) + start;
        }

        public static float easeInCirc(float start, float end, float value)
        {
            end -= start;
            return -end * (Mathf.Sqrt(1 - value * value) - 1) + start;
        }

        public static float easeOutCirc(float start, float end, float value)
        {
            value--;
            end -= start;
            return end * Mathf.Sqrt(1 - value * value) + start;
        }

        public static float easeInOutCirc(float start, float end, float value)
        {
            value /= .5f;
            end -= start;
            if (value < 1) return -end * 0.5f * (Mathf.Sqrt(1 - value * value) - 1) + start;
            value -= 2;
            return end * 0.5f * (Mathf.Sqrt(1 - value * value) + 1) + start;
        }

        /* GFX47 MOD START */
        public static float easeInBounce(float start, float end, float value)
        {
            end -= start;
            var d = 1f;
            return end - easeOutBounce(0, end, d - value) + start;
        }
        /* GFX47 MOD END */

        /* GFX47 MOD START */
        //public static float bounce(float start, float end, float value){
        public static float easeOutBounce(float start, float end, float value)
        {
            value /= 1f;
            end -= start;
            if (value < 1 / 2.75f) return end * (7.5625f * value * value) + start;

            if (value < 2 / 2.75f)
            {
                value -= 1.5f / 2.75f;
                return end * (7.5625f * value * value + .75f) + start;
            }

            if (value < 2.5 / 2.75)
            {
                value -= 2.25f / 2.75f;
                return end * (7.5625f * value * value + .9375f) + start;
            }

            value -= 2.625f / 2.75f;
            return end * (7.5625f * value * value + .984375f) + start;
        }
        /* GFX47 MOD END */

        /* GFX47 MOD START */
        public static float easeInOutBounce(float start, float end, float value)
        {
            end -= start;
            var d = 1f;
            if (value < d * 0.5f) return easeInBounce(0, end, value * 2) * 0.5f + start;
            return easeOutBounce(0, end, value * 2 - d) * 0.5f + end * 0.5f + start;
        }
        /* GFX47 MOD END */

        public static float easeInBack(float start, float end, float value)
        {
            end -= start;
            value /= 1;
            var s = 1.70158f;
            return end * value * value * ((s + 1) * value - s) + start;
        }

        public static float easeOutBack(float start, float end, float value)
        {
            var s = 1.70158f;
            end -= start;
            value = value - 1;
            return end * (value * value * ((s + 1) * value + s) + 1) + start;
        }

        public static float easeInOutBack(float start, float end, float value)
        {
            var s = 1.70158f;
            end -= start;
            value /= .5f;
            if (value < 1)
            {
                s *= 1.525f;
                return end * 0.5f * (value * value * ((s + 1) * value - s)) + start;
            }

            value -= 2;
            s *= 1.525f;
            return end * 0.5f * (value * value * ((s + 1) * value + s) + 2) + start;
        }

        public static float punch(float amplitude, float value)
        {
            float s = 9;
            if (value == 0)
                return 0;
            if (value == 1) return 0;
            var period = 1 * 0.3f;
            s = period / (2 * Mathf.PI) * Mathf.Asin(0);
            return amplitude * Mathf.Pow(2, -10 * value) * Mathf.Sin((value * 1 - s) * (2 * Mathf.PI) / period);
        }

        /* GFX47 MOD START */
        public static float easeInElastic(float start, float end, float value)
        {
            end -= start;

            var d = 1f;
            var p = d * .3f;
            float s = 0;
            float a = 0;

            if (value == 0) return start;

            if ((value /= d) == 1) return start + end;

            if (a == 0f || a < Mathf.Abs(end))
            {
                a = end;
                s = p / 4;
            }
            else
            {
                s = p / (2 * Mathf.PI) * Mathf.Asin(end / a);
            }

            return -(a * Mathf.Pow(2, 10 * (value -= 1)) * Mathf.Sin((value * d - s) * (2 * Mathf.PI) / p)) + start;
        }
        /* GFX47 MOD END */

        /* GFX47 MOD START */
        //public static float elastic(float start, float end, float value){
        public static float easeOutElastic(float start, float end, float value)
        {
            /* GFX47 MOD END */
            //Thank you to rafael.marteleto for fixing this as a port over from Pedro's UnityTween
            end -= start;

            var d = 1f;
            var p = d * .3f;
            float s = 0;
            float a = 0;

            if (value == 0) return start;

            if ((value /= d) == 1) return start + end;

            if (a == 0f || a < Mathf.Abs(end))
            {
                a = end;
                s = p * 0.25f;
            }
            else
            {
                s = p / (2 * Mathf.PI) * Mathf.Asin(end / a);
            }

            return a * Mathf.Pow(2, -10 * value) * Mathf.Sin((value * d - s) * (2 * Mathf.PI) / p) + end + start;
        }

        /* GFX47 MOD START */
        public static float easeInOutElastic(float start, float end, float value)
        {
            end -= start;

            var d = 1f;
            var p = d * .3f;
            float s = 0;
            float a = 0;

            if (value == 0) return start;

            if ((value /= d * 0.5f) == 2) return start + end;

            if (a == 0f || a < Mathf.Abs(end))
            {
                a = end;
                s = p / 4;
            }
            else
            {
                s = p / (2 * Mathf.PI) * Mathf.Asin(end / a);
            }

            if (value < 1)
                return -0.5f * (a * Mathf.Pow(2, 10 * (value -= 1)) * Mathf.Sin((value * d - s) * (2 * Mathf.PI) / p)) +
                       start;
            return a * Mathf.Pow(2, -10 * (value -= 1)) * Mathf.Sin((value * d - s) * (2 * Mathf.PI) / p) * 0.5f + end +
                   start;
        }

        public static List<Vector3> SoftenOutline(Vector3[] l)
        {
            var list = new List<Vector3>();
            for (int i = 1; i < l.Length - 1; i++)
            {
                list.Add(SBLP(l[i - 1], l[i], l[i + 1], 0.5f));
            }
            list.Add(SBLP(l[l.Length - 2], l[l.Length - 1], l[0], 0.5f));
            list.Add(SBLP(l[l.Length - 1], l[0], l[1], 0.5f));

            return list;
        }

        public static List<Vector3> SerrationLine(Vector3 start, Vector3 end, int times)
        {
            var distance = Vector3.Distance(start, end) / times;
            var d = Vector3.Normalize(end - start);
            var list = new List<Vector3>();
            list.Add(start);
            for (int i = 1; i < times; i ++)
            {
                var p = list[list.Count - 1];
                var t = (i >= 2 ? i % 2 : 1) > 0;
                var n = p + (distance * d);
                list.Add(new Vector3(t ? p.x : n.x, p.y, t ? n.z : p.z));
                list.Add(n);
            }
            return list;
        }

        public static bool CompareVersion(string version, string appVersion)
        {
            string[] s1 = version.Split('.');
            string[] s2 = appVersion.Split('.');
            for (int i = 0; i < s1.Length; i++)
            {
                if (s2.Length <= i) continue;
                if (Convert.ToInt32(s1[i]) > Convert.ToInt32(s2[i])) return false;
            }
            return true;
        }

        public static bool InArray(JSONArray arr, JSONNode node)
        {
            for (int i = 0; i < arr.Count; i++)
            {
                if (arr[i] == node) return true;
            }

            return false;
        }
    }
}

public class AsceOrder : IComparer<object[]>
{
    public int index = 0;
    public int Compare(object[] x, object[] y)
    {
        Debug.LogErrorFormat("{0} {1}", x[index], y[index]);
        return (int)x[index] - (int)y[index];
    }
}